using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Nop.Core.Infrastructure;
using Nop.Plugin.ActiveForever.ActiveChat.Data;
using Nop.Plugin.ActiveForever.ActiveChat.Models;
using Nop.Plugin.ActiveForever.ActiveChat.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Xml.Linq;

namespace Nop.Plugin.ActiveForever.ActiveChat.Hubs
{
	[HubName("livechat")]
	public class LiveChatHub : Hub
	{
		private IActiveChatService activeChatService;

		public static List<Client> clients;

		public static List<Operator> operators;

		private static string _chargeId;

		static LiveChatHub()
		{
			clients = new List<Client>();
			operators = new List<Operator>();
			_chargeId = string.Empty;
		}

		public void CheckStatus(string clientIp)
		{
			activeChatService = EngineContext.Current.Resolve<IActiveChatService>();
			if ((
			    from c in activeChatService.GetAllBannedClients()
			    where c.ClientIp == clientIp
			    select c).Any())
			{
				Clients.Caller.liveChatStatus("offline");
				return;
			}
			Clients.All.liveChatStatus((operators.Any() ? "online" : "offline"));
		}

		public void EndChat(string connectionId)
		{
			Client client = clients.SingleOrDefault(c => c.ConnectionId == Context.ConnectionId);
			if (client != null)
			{
				string str = connectionId;
				Guid sessionId = client.SessionId;
				SendMessage("Your chat session has ended.", str, "", "System", "info", sessionId.ToString());
				clients.Remove(client);
				if (!string.IsNullOrEmpty(client.OperatorConnectionId))
				{
					string operatorConnectionId = client.OperatorConnectionId;
					Guid guid = client.SessionId;
					SendMessage("Your chat session has ended.", operatorConnectionId, "", "System", "info", guid.ToString());
					Operator @operator = operators.SingleOrDefault(o => o.ConnectionId == client.OperatorConnectionId);
					@operator.InChat = false;
					ClientChatLog clientChatLog = new ClientChatLog
					{
						Browser = client.Browser,
						ClientIp = client.Ip,
						ConnectedAt = client.ConnectedAt,
						Email = client.Email,
						Name = client.Name,
						OperatingSystem = client.OperatingSystem,
						SessionId = client.SessionId,
						Url = client.Url
					};
					XElement xElement = new XElement("Transcript", 
						from x in client.Transcript
						select new XElement("Message", new object[] { new XAttribute("SessionId", x.SessionId), new XAttribute("TimeStamp", x.TimeStamp), new XAttribute("Sender", x.Sender), new XAttribute("Recipient", x.Recipient), new XAttribute("Message", x.Message) }));
					clientChatLog.Transcript = xElement.ToString();
					activeChatService = EngineContext.Current.Resolve<IActiveChatService>();
					activeChatService.InsertClientChatLog(clientChatLog);
					if (!string.IsNullOrEmpty(clientChatLog.Email))
					{
						try
						{
							activeChatService.SendPostChatEmail(clientChatLog.Transcript, clientChatLog.Name, @operator.Name, clientChatLog.Email);
						}
						catch
						{
						}
					}
				}
			}
			LoadAllClients();
			LoadAllOperators();
		}

		public void IsTyping(string sourceConnectionId, string destinationConnectionId)
		{
			Clients.Client(destinationConnectionId).isTyping();
		}

		public void Join(Client newCLient)
		{
			activeChatService = EngineContext.Current.Resolve<IActiveChatService>();
			if ((
			    from c in activeChatService.GetAllBannedClients()
			    where c.ClientIp == newCLient.Ip
			    select c).Any())
			{
				Clients.Caller.liveChatStatus("offline");
				return;
			}
			newCLient.SessionId = Guid.NewGuid();
			newCLient.Transcript = new List<TranscriptMessage>();
			if (!(
			    from c in clients
			    where c.ConnectionId == newCLient.ConnectionId
			    select c).Any())
			{
				clients.Add(newCLient);
			}
			LoadAllClients();
			PlayOperatorNotification();
		}

		public void KeepAlive()
		{
			try
			{
				Clients.All.sessionUpdate((HttpContext.Current.User != null));
			}
			catch
			{
			}
		}

		public void LoadAllClients()
		{
		    Clients.AllExcept(clients.Select(client => client.ConnectionId).ToArray()).loadAllClients(clients);
		}

		public void LoadAllOperators()
		{
		    Clients.AllExcept(clients.Select(client => client.ConnectionId).ToArray()).loadAllOperators(operators);
		}

		public override Task OnDisconnected()
		{
			Operator @operator = operators.SingleOrDefault(o => o.ConnectionId == Context.ConnectionId);
			if (@operator != null)
			{
				try
				{
					Client empty = clients.SingleOrDefault(c => c.OperatorConnectionId == Context.ConnectionId);
					empty.OperatorConnectionId = string.Empty;
					EndChat(empty.ConnectionId);
				}
				catch
				{
				}
				operators.Remove(@operator);
			}
			Client client = clients.SingleOrDefault(c => c.ConnectionId == Context.ConnectionId);
			if (client == null)
			{
				LoadAllClients();
			}
			else
			{
				EndChat(client.ConnectionId);
				clients.Remove(client);
			}
			LoadAllOperators();
			return base.OnDisconnected();
		}

		public void OperatorJoin(string userName, string chargeId)
		{
			_chargeId = chargeId;
			if (!string.IsNullOrEmpty(chargeId) || LiveChatHub.operators.Count != 1)
			{
				if (!(
				    from o in LiveChatHub.operators
				    where o.Name == userName
				    select o).Any())
				{
					List<Operator> operators = LiveChatHub.operators;
					Operator @operator = new Operator
					{
						Name = userName,
						ConnectionId = Context.ConnectionId
					};
					operators.Add(@operator);
				}
				LoadAllOperators();
				return;
			}
			(Clients.Caller).showUpgradeDialog();
		}

		public void PlayOperatorNotification()
		{
			if (!string.IsNullOrEmpty(_chargeId))
			{
			    Clients.AllExcept(clients.Select(client => client.ConnectionId).ToArray()).playOperatorNotification(operators);
			}
		}

		public void SendMessage(string message, string recipientId, string senderId, string senderName, string messageType, string sessionId)
		{
			Clients.Client(recipientId).postMessage(message, senderId, senderName, messageType);
			Client client = clients.SingleOrDefault(c => c.SessionId == Guid.Parse(sessionId));
			if (client != null)
			{
				string name = client.Name;
				if (client.ConnectionId != recipientId)
				{
					Operator @operator = operators.SingleOrDefault(o => o.ConnectionId == recipientId);
					if (@operator != null)
					{
						PlayOperatorNotification();
						name = @operator.Name;
					}
				}
				TranscriptMessage transcriptMessage = new TranscriptMessage
				{
					Message = message,
					Recipient = name,
					Sender = senderName,
					SessionId = Guid.Parse(sessionId),
					TimeStamp = DateTime.Now
				};
				client.Transcript.Add(transcriptMessage);
			}
		}

		public void SendUrl(string url, string clientConnectionId)
		{
			Clients.Client(clientConnectionId).receiveWebLink(url);
		}

		public void StartClientChat(string clientConnectionId, string operatorConnectionId)
		{
			Client client = clients.SingleOrDefault(c => c.ConnectionId == clientConnectionId);
			if (client != null)
			{
				Operator sessionId = operators.SingleOrDefault(o => o.ConnectionId == operatorConnectionId);
				if (sessionId != null)
				{
					sessionId.InChat = true;
					sessionId.SessionId = client.SessionId;
					client.OperatorConnectionId = operatorConnectionId;
					string str = string.Concat("You are now chatting with <strong>", sessionId.Name, "</strong>");
					string str1 = clientConnectionId;
					Guid guid = client.SessionId;
					SendMessage(str, str1, "", "System", "info", guid.ToString());
					Clients.Caller.updateSessionId(client.SessionId);
					Clients.Client(client.ConnectionId).updateSessionId(client.SessionId);
					LoadAllClients();
					LoadAllOperators();
				}
			}
		}

		public void TransferOperator(string clientConnectionId, string operatorConnectionId)
		{
			if (clients.SingleOrDefault(c => c.ConnectionId == clientConnectionId) != null)
			{
				Operator @operator = operators.SingleOrDefault(o => o.ConnectionId == operatorConnectionId);
				if (@operator != null)
				{
					bool inChat = @operator.InChat;
				}
			}
		}
	}
}